ICO: coffee prices

Indicative coffee prices

library(data.table) #tabular data manipulation library
data.table 1.11.4  Latest news: http://r-datatable.com
library(here)       #project file system organizational library
here() starts at /Users/denis/repos/coffee_prices
library(ggplot2)    #plotting
library(knitr)      #RMarkdown knitting library
library(gridExtra)  #Additional functions for ggplot2
library(kableExtra) #Table styling for RMarkdown
library(fpp2)       #Time Series modeling library
Loading required package: forecast
Loading required package: fma
Loading required package: expsmooth
library(scales)     #Formatting functions
library(urca)
library(tempdisagg)
source(here::here("scripts/data processing/data_processing.R"))

Attaching package: ‘lubridate’

The following object is masked from ‘package:here’:

    here

The following objects are masked from ‘package:data.table’:

    hour, isoweek, mday, minute, month, quarter, second, wday, week, yday, year

The following object is masked from ‘package:base’:

    date


Attaching package: ‘zoo’

The following objects are masked from ‘package:base’:

    as.Date, as.Date.numeric
prices = read_indicative_prices()
'measure.vars' [1960, 1961, 1962, 1963, ...] are not all of the same type. By order of hierarchy, the molten data value column will be of type 'double'. All measure variables not of type 'double' will be coerced too. Check DETAILS in ?melt.data.table for more on coercion.
grower_prices = read_grower_prices()
'measure.vars' [1960, 1961, 1962, 1963, ...] are not all of the same type. By order of hierarchy, the molten data value column will be of type 'double'. All measure variables not of type 'double' will be coerced too. Check DETAILS in ?melt.data.table for more on coercion.
exportable_production = read_exportable_production()
total_production = read_total_production()
consumption = read_consumption()
inventories = read_inventories()
coffee_types = unique(total_production$CoffeeType)
plot_coffee_ts = function(ts_list, measure){
  plot_list = 
    lapply(
      coffee_types,
      function(coffee_type){
        autoplot(ts_list[[coffee_type]])+
          theme_minimal()+
          xlab("")+
          ylab(measure)+
          ggtitle(coffee_type)
      }
    )
  
  do.call("grid.arrange", c(plot_list, ncol = 2))
}
ggplot(prices)+
  geom_line(aes(x = Month, y=Indicative_Price, color=CoffeeType))+
  theme_minimal()+
  scale_x_date(date_breaks = "1 year", date_labels = "%Y")+
  xlab("")+
  ylab("Price in US cents/lb")+
  ggtitle("Nominal coffee indicative price reported by ICO")

Looking at the price graph for the past 27 years it is obvious that coffee prices for different varieties are closely correlated. But robustas are diverged slightly from the arabicas in the past 10 years and not have such strong spikes in the prices.

Since we are working on price data, to avoid inflation related issues we should normalize prices.
World Bank’s Consumer Price Index data was used for rescaling.

ggplot(prices)+
  geom_line(aes(x = Month, y=Constant2010Price, color=CoffeeType))+
  theme_minimal()+
  scale_x_date(date_breaks = "1 year", date_labels = "%Y")+
  xlab("")+
  ylab("Price in US cents/lb")+
  ggtitle("Constant 2010 coffee indicative price reported by ICO")

Going forward all modeling and evaluation of price will be done using Constant 2010 USD.

As with majority of price time series, they

ggAcf(diff(prices[CoffeeType=="Robustas"]$Constant2010Price))

If we look and Cross-Correlation between price time series we will see an almost 1 correlation at lag 0 for majority of arabicas and near 0.6-0.8 correlation of Robustas with Arabicas variations.

ts_price_list = 
  lapply(
    coffee_types,
    function(name){
      return(
        ts(prices[CoffeeType == name][order(Month)]$Constant2010Price,
             start = min(year(prices$Month)), frequency = 12
        )
      )
    }
  )
names(ts_price_list) = coffee_types
plot_list = list()
i = 1;
for (coffee_type in names(ts_price_list)){
  for (coffee_type_2 in names(ts_price_list)){
    p = ggCcf(unlist(ts_price_list[[coffee_type]]), unlist(ts_price_list[[coffee_type_2]]))+
      theme_minimal()+
      ggtitle("")+
      xlab("")+
      ylab("")
    
    plot_list[[i]] = p
    i = i + 1
  }
}
do.call("grid.arrange", c(plot_list, ncol = 4, top = "Robustas, Brazilian Naturals, Other Milds, Colombian Milds"))

Prices paid to growers

Similarly to indicative prices, we rescaled prices paid to growers to Constant 2010 USD.
Prices paid to growers vary significantly across exporter countries.

grower_prices[Year == 2016][, .(
  MinPrice = min(Price), 
  AvgPrice = mean(Price), 
  PriceSd = sd(Price),
  MaxPrice = max(Price)
), by=CoffeeType]%>%
  kable(format="html", col.names = c("Coffee","Min", "Avg", "SD", "Max"))%>%
  kable_styling(bootstrap_options = c("striped"), full_width = FALSE)%>%
  add_header_above(header = c(" "=1, "Price" = 4))
Price
Coffee Min Avg SD Max
Colombian Milds 123.5571 123.55710 NA 123.5571
Other Milds 57.5014 139.27180 70.23651 271.7944
Brazilian Naturals 87.3074 98.53067 10.39530 107.8289
Robustas 21.5373 58.05416 24.43416 92.7763

Prices paid to growers are available on yearly average level, but even on this level we see that grower prices strongly correlate with international export prices and it is a question whether high grower prices push the international prices up or higher profit margins on international trade allows for higher grower prices to be paid locally.

scaled_grower_prices = merge(
  grower_prices, total_production,
  by.x = c("Country", "CoffeeType", "Year"), by.y = c("Country", "CoffeeType", "Year"),
  all.x = TRUE, all.y = FALSE
)
scaled_grower_prices = 
  scaled_grower_prices[,.(
    Price = sum(Constant2010Price * TotalProduction)/sum(TotalProduction)
  ), by = .(CoffeeType, Year)]
ts_grower_prices = 
  lapply(
    coffee_types,
    function(name){
      return(ts(scaled_grower_prices[CoffeeType == name][order(Year)]$Price, 
                start = min(scaled_grower_prices$Year)))
    }
  )
names(ts_grower_prices) = coffee_types
ts_grower_prices_monthly = 
  lapply(
    coffee_types, 
    function(name){
      tss = ts_grower_prices[[name]]
      tda = td(tss~1, conversion="average", to = "monthly", method="denton-cholette")
      return(predict(tda))
    }
  )
names(ts_grower_prices_monthly) = coffee_types
ggplot(scaled_grower_prices)+
  geom_line(aes(x = Year, y=Price, color=CoffeeType))+
  theme_minimal()+
  scale_x_continuous(breaks = seq(min(scaled_grower_prices$Year), max(scaled_grower_prices$Year)))+
  xlab("")+
  ylab("Price in US cents/lb")+
  ggtitle("Total production weighted growers price for coffee types")

Production

Certainly coffee prices are also dependent on supply side of the market dynamics. Over the past 27 years production of Brazilian Naturals and Robustas almost trippled, while production of Colombian Milds have stagnated.

production_year_coffee = total_production[,.(TotalProduction = sum(TotalProduction)), by=.(Year, CoffeeType)]
exportable_production_year_coffee = exportable_production[,.(TotalProduction = sum(TotalProduction)),
                                                          by=.(Year,CoffeeType)]
ggplot(production_year_coffee)+
  geom_line(aes(x=Year, y = TotalProduction, group=CoffeeType, color=CoffeeType))+
  scale_y_continuous(labels = comma)+
  ylab("Total production in thousands 60kg bags")+
  theme_minimal()

Consumption

From the demand side ICO reports the following dynamics.
It appears that consumption increases almost linearly with very minor variation due to economic factors. Judging by the graphs it might not be necessary to model consumption of a particular coffee type as a function of world GDP.

consumption_year_coffee = consumption[, .(Consumption = sum(Consumption)), by=.(Year, CoffeeType)]
ggplot(consumption_year_coffee)+
  geom_line(aes(x=Year, y=Consumption, group=CoffeeType, color=CoffeeType))+
  xlab("")+
  ylab("Consumption in thousand 60kg bags")+
  scale_y_continuous(labels = comma)+
  scale_x_continuous(breaks = seq(min(consumption_year_coffee$Year), max(consumption_year_coffee$Year)))+
  ggtitle("Worldwide consumption of coffee types")+
  theme_minimal()

tmp = consumption[, .(Consumption = sum(Consumption)), by=.(Year, Country)]
ggplot(tmp)+
  geom_line(aes(x=Year, y=Consumption, group=Country, color=Country))+
  xlab("")+
  ylab("Consumption in thousand 60kg bags")+
  scale_y_continuous(labels = comma)+
  scale_x_continuous(breaks = seq(min(tmp$Year), max(tmp$Year)))+
  ggtitle("Worldwide consumption of coffee types")+
  theme_minimal()

Inventories

ICO reports end of the year inventories at importer countries.
If we aggregate by coffee type we get the following available inventories.

inventories_year_coffee = inventories[, .(Inventory = sum(Inventory)), by=.(Year, CoffeeType)]
ggplot(inventories_year_coffee)+
  geom_line(aes(x=Year, y=Inventory, group=CoffeeType, color=CoffeeType))+
  xlab("")+
  ylab("Inventory in thousand 60kg bags")+
  scale_y_continuous(labels = comma)+
  scale_x_continuous(breaks = seq(min(inventories_year_coffee$Year), max(inventories_year_coffee$Year)))+
  ggtitle("Worldwide inventory of coffee types")+
  theme_minimal()

Predicting coffee price

Price seasonality and trend

To decompose price time series into trend and seasonal components we will use STL method, which stands for “Seasonal and Trend decomposition using Loess”.

plot_list = list()
for (name in names(ts_price_list)){
  plot_list[[name]] = 
    ts_price_list[[name]] %>%
    stl(t.window = 13, s.window = "periodic", robust=TRUE)%>%
    autoplot()+
    ggtitle(name)+
    theme_minimal()    
}
do.call("grid.arrange", c(plot_list, ncol = 2))

From the graphs it is apparent that the seasonal component is not very strong in coffee price time series. This observation can be validated by measuring strength of trend/seasonal components in relation to residual.
\[ F_{t}=max(0,1-\frac{Var(R_t)}{Var(T_t+R_t)}) \] where \(F_t\) - strength of trend, \(R_t\) residuals and \(T_t\) is trend.

robusta_stl = stl(ts_price_list[["Robustas"]], t.window = 13, s.window = "periodic", robust=TRUE)
trend_strength = function(stl_result){
  return(
    max(
        0, 
        1 - var(stl_result$time.series[,"remainder"])/
              var(stl_result$time.series[,"trend"]+stl_result$time.series[,"remainder"])
    )
  )
}
seasonal_strength = function(stl_result){
  return(
    max(
        0, 
        1 - var(stl_result$time.series[,"remainder"])/
              var(stl_result$time.series[,"seasonal"]+stl_result$time.series[,"remainder"])
    )
  )
}
rbindlist(
lapply(coffee_types, 
        function(name){
  stl_result = stl(ts_price_list[[name]], t.window = 13, s.window = "periodic", robust=TRUE)
  return(list(
    CoffeeType = name,
    TrendStrength = trend_strength(stl_result),
    SeasonalStrength = seasonal_strength(stl_result)
  ))
}))%>%
  kable(format="html", col.names = c("Coffee Type", "Trend strength", "Seasonal strength"))%>%
  kable_styling(bootstrap_options = c("striped"), full_width = FALSE)
Coffee Type Trend strength Seasonal strength
Robustas 0.9200180 0.0011611
Brazilian Naturals 0.9124249 0.0154484
Other Milds 0.9091719 0.0000000
Colombian Milds 0.9032300 0.0076221

The seasonality strength is minimal in all price time series, as a result most of disagregations for other time series can ignore seasonality and be done using linear interpolations.

Disaggregating yearly data to monthly level

Since our price data is on a monthly level it is preferable to model the prices on the same level. On another hand majority of our production and consumption data is on a yearly aggregated level.
There is a variety of statistical methods to disaggregate yearly data to monthly data, depending on a seasonality assumptions.

Trend-seasonal decomposition of price time series showed that price is does not have a strong seasonal component as a result we will use the basic disaggregation model.

Price to growers

Consumption

It is fair to assume that consumption of coffee doesn’t have strong seasonal characteristics as such disaggregation model does not require seasonal indicator series.

ts_consumption_list = 
  lapply(
    coffee_types,
    function(coffee_type){
      return(
        ts(consumption_year_coffee[CoffeeType == coffee_type][order(Year)]$Consumption,
           start = min(consumption_year_coffee$Year))
      )
    }
  )
names(ts_consumption_list) = coffee_types
ts_consumption_monthly_list = 
  lapply(
    coffee_types,
    function(coffee_type){
      tsd = ts_consumption_list[[coffee_type]]
      return(predict(td(tsd ~ 1, to = "monthly", method = "denton-cholette", conversion = "sum")))
    }
  )
names(ts_consumption_monthly_list) = coffee_types
plot_list = 
  lapply(
    coffee_types,
    function(coffee_type){
      autoplot(ts_consumption_monthly_list[[coffee_type]])+
        theme_minimal()+
        xlab("")+
        ylab("Consumption")+
        ggtitle(coffee_type)
    }
  )
do.call("grid.arrange", c(plot_list, ncol = 2))

Exportable production

ts_production_list = 
  lapply(
    coffee_types,
    function(coffee_type){
      return(
        ts(exportable_production_year_coffee[CoffeeType == coffee_type][order(Year)]$TotalProduction,
         start = min(production_year_coffee$Year)
        )
      )
    }
)
names(ts_production_list) = coffee_types
ts_production_monthly_list = 
  lapply(
    coffee_types,
    function(coffee_type){
      tsd = ts_production_list[[coffee_type]]
      return(predict(td(tsd ~ 1, to = "monthly", method = "denton-cholette", conversion = "sum")))
    }
  )
names(ts_production_monthly_list) = coffee_types
plot_list = 
  lapply(
    coffee_types,
    function(coffee_type){
      autoplot(ts_production_monthly_list[[coffee_type]])+
        theme_minimal()+
        xlab("")+
        ylab("Production")+
        ggtitle(coffee_type)
    }
  )
do.call("grid.arrange", c(plot_list, ncol = 2))

Stocks + Production - Consumption

A useful derived measure, that could be predictive of price fluctuations would be available inventory of coffee at particular month.
ICO reports on inventories at the end of the year in importer countries, we can disagregate end of the year stocks by indicative time series of cumulative production - consumption. A predictor then would be a ratio between available stock and predicted yearly consumption.

Production - Consumption cumulatively within a year

Difference between prodution and consumption (Stock delta):

ts_stock_delta_list = 
  lapply(
    coffee_types, 
    function(name){
      return(ts_production_monthly_list[[name]] - ts_consumption_monthly_list[[name]])
    }
  )
names(ts_stock_delta_list) = coffee_types
plot_list = 
  lapply(
    coffee_types,
    function(coffee_type){
      autoplot(ts_stock_delta_list[[coffee_type]])+
        theme_minimal()+
        xlab("")+
        ylab("Stock delta")+
        ggtitle(coffee_type)
    }
  )
do.call("grid.arrange", c(plot_list, ncol = 2))

Monthly stock estimates

cumsum_by_years = function(ts){
  months = factor(cycle(ts), levels=1:12)
  m = tapply(ts, list(year = floor(time(ts)), month=months), c)
  m_sum = t(apply(m, 1, cumsum))
  #m_sum = t(apply(m_sum, 1, scale))
  return(ts(c(t(m_sum)), start = 1990, frequency = 12))
}
ts_stock_delta_cum_list = 
  lapply(
    coffee_types, 
    function(name){
      cumsum_by_years(ts_stock_delta_list[[name]])
    }
  )
names(ts_stock_delta_cum_list) = coffee_types
ts_stock_list = 
  lapply(
    coffee_types,
    function(coffee_type){
      return(
        ts(inventories_year_coffee[CoffeeType == coffee_type][order(Year)]$Inventory,
         start = min(inventories_year_coffee$Year)
        )
      )
    }
)
names(ts_stock_list) = coffee_types
ts_stock_monthly_list = 
  lapply(
    coffee_types,
    function(coffee_type){
      tsd = ts_stock_list[[coffee_type]]
      stock_delta = ts_stock_delta_cum_list[[coffee_type]]
      return(predict(td(tsd ~ 1, 
                        to = "monthly", 
                        conversion = "last", 
                        method = "denton-cholette",
                        criterion = "proportional")))
    }
  )
names(ts_stock_monthly_list) = coffee_types
plot_coffee_ts(ts_stock_monthly_list, "Stock monthly")

Stock to consumption ratio

ts_stock_to_consumption_monthly_list = 
  lapply(
    coffee_types,
    function(coffee_type){
      return(
        ts_stock_monthly_list[[coffee_type]] / ts_consumption_monthly_list[[coffee_type]]
      )
    }
)
names(ts_stock_to_consumption_monthly_list) = coffee_types
plot_coffee_ts(ts_stock_to_consumption_monthly_list, "Stock/Consumption")

Price models

We will start modeling coffee price with the simplest models to set a benchmark and then will slowly add seasonality and covariate complexity while controling predictive accuracy.

Accuracy

For time series forecasting accuracy testing we will use cross-validation procedure on rolling forecasting origin with window of 4 (will be predicting 4 months ahead).
Accuracy will be measured using RMSE.
optional caption text

ARIMA

Let’s start by modeling coffee prices as separate time series using ARIMA without any covariates.
First we need to check if our time series is non-stationary and requires differencing, we will do that by applying a series of KPSS tests.

p1_test = ts_price_list[["Robustas"]]%>%ur.kpss()
p2_test = ts_price_list[["Robustas"]] %>% diff() %>% ur.kpss()
p1 = ts_price_list[["Robustas"]]%>%autoplot()+
  theme_minimal()+
  ggtitle(paste0("Price, KPSS statistic: ", round(p1_test@teststat, 4), ", 1% sig. ", p1_test@cval[4]))
p2 = ts_price_list[["Robustas"]]%>%diff()%>%autoplot()+
  theme_minimal()+
  ggtitle(paste0("First difference, KPSS statistic: ", round(p2_test@teststat, 4), ", 1% sig. ", p2_test@cval[4]))
grid.arrange(p1, p2, ncol=1)

differencing_required = ndiffs(ts_price_list[["Robustas"]])
seasonal_differencing_required = nsdiffs(ts_price_list[["Robustas"]])

KPSS tests showed that 1 main differencing is required, but no seasonal differencing is required for price time series (mostly due to negligible seasonal effects).

ARIMA models have order parameters {p, d, q}:
- p - order of autoregressive part
- d - degree of first differencing involved
- q - order of the moving average part

cutoff_date = c(2016,6)
plot_list = 
  lapply(
    coffee_types,
    function(name){
      a = auto.arima(window(ts_price_list[[name]], end=cutoff_date))
      #a = forecast::Arima(window(ts_price_list[[name]], end=cutoff_date), order = c(6,1,0))
      order = arimaorder(a)
      
      return(
        autoplot(ts_price_list[[name]])+
          autolayer(forecast(a, h=24), 
                    series=paste0("ARIMA(", paste(arimaorder(a), collapse=","), ")"), 
                    PI=FALSE)+
          theme_minimal()
      )
    }
  )
do.call("grid.arrange", c(plot_list, ncol = 2))

Baseline accuracy results:

basic_arima_forecast = function(ts, h){
  arima_fit = Arima(ts, order = c(2,1,2))
  return(forecast(arima_fit, h = h))
}
test_accuracy = function(ts, forecast_func, name, ...){
  e = tsCV(ts, forecast_func, h=4, window = 120, ...)
  RMSE <-
    e^2 %>% mean(na.rm = TRUE) %>% sqrt()
  
  return(data.table(Model = name, RMSE = RMSE)) 
}
tests =
rbindlist(
  lapply(
    coffee_types, 
    function(name){
      return(test_accuracy(ts_price_list[[name]], basic_arima_forecast, "ARIMA(2,1,2)")[, CoffeeType:=name])
    }
  )
)
tests %>% data.table::dcast(
  Model ~ CoffeeType, value.var = "RMSE"
) %>%
  kable(type = "html")%>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped"))
Model Brazilian Naturals Colombian Milds Other Milds Robustas
ARIMA(2,1,2) 15.81667 17.78024 16.94567 7.831789

Adding covariates - ARIMAX

Price cross-correlations

Hypothesis: price time series for various coffee types are cointegrated.

jotest = urca::ca.jo(
  data.frame(
    BrazilianNaturals = diff(as.vector(ts_price_list[["Brazilian Naturals"]])),
    ColombianMilds = diff(as.vector(ts_price_list[["Colombian Milds"]])),
    OtherMilds = diff(as.vector(ts_price_list[["Other Milds"]]))
  ),
  type = "trace",
  K=2,
  ecdet = "none",
  spec = "transitory"
)
summary(jotest)

###################### 
# Johansen-Procedure # 
###################### 

Test type: trace statistic , with linear trend 

Eigenvalues (lambda):
[1] 0.5259469 0.2846987 0.2257574

Values of teststatistic and critical values of test:

           test 10pct  5pct  1pct
r <= 2 |  86.74  6.50  8.18 11.65
r <= 1 | 200.32 15.66 17.95 23.52
r = 0  | 453.36 28.71 31.52 37.22

Eigenvectors, normalised to first column:
(These are the cointegration relations)

                     BrazilianNaturals.l1 ColombianMilds.l1 OtherMilds.l1
BrazilianNaturals.l1             1.000000         1.0000000      1.000000
ColombianMilds.l1               -4.179288         0.3891362     -1.570299
OtherMilds.l1                    3.088840        -1.3400994      2.563959

Weights W:
(This is the loading matrix)

                    BrazilianNaturals.l1 ColombianMilds.l1 OtherMilds.l1
BrazilianNaturals.d            0.2039688       -0.09821647    -0.3253757
ColombianMilds.d               0.7076774        0.36917546    -0.2980944
OtherMilds.d                   0.3364528        0.51621622    -0.3144065

Test statistic at r = 0, shows that there

Price to Growers

All of the following cross correlation analysis is done using “pre-whitening” approach, with the following steps:
1. Determine a time series model for the x-variable and store the residuals from this model.
2. Filter the y-variable series using the x-variable model (using the estimated coefficients from step 1). In this step we find differences between observed y-values and “estimated” y-values based on the x-variable model.
3. Examine CFF between the residuals from Step 1 and the filtered y-values from Step 2. This CFF can be used to identify the possible terms for a lagged regression.

check_ccf = function(ts_list){
  lapply(
    coffee_types,
    function(name){
      pw = TSA::prewhiten(
        ts_list[[name]],
        ts_price_list[[name]]
      )
      
      p = 
        autoplot(pw$ccf)+
          theme_minimal()+
          ggtitle(name)
      
      return(p)
    }
  )
}
plot_list = check_ccf(ts_grower_prices_monthly)

do.call("grid.arrange", c(plot_list, ncol = 2))

Production

Let’s check for optimal lag using CCF:

plot_list = check_ccf(ts_production_monthly_list)

do.call("grid.arrange", c(plot_list, ncol = 2))

There is no definitive correlation structure between production and price for all coffee types, so most likely correlation that we see are spurious.

Stock to Consumption

Let’s check for optimal lag using CCF:

plot_list = check_ccf(ts_stock_to_consumption_monthly_list)

do.call("grid.arrange", c(plot_list, ncol = 2))

Very interesting correlation structure, that says that price today is based on the estimated available stock 6 months down the line. The only exception is Colombian Milds, which don’t show strong correlations within reasonable lags.

coffee_type = "Brazilian Naturals"
lagged_stock_consumption = data.table(
  StockConsumptionLag0 = as.vector(ts_stock_to_consumption_monthly_list[[coffee_type]])
)
lagged_stock_consumption[, `:=`(
  StockConsumptionLag1 = data.table::shift(StockConsumptionLag0, type = "lag", n=6),
  StockConsumptionLag2 = data.table::shift(StockConsumptionLag0, type = "lag", n=12),
  StockConsumptionLag3 = data.table::shift(StockConsumptionLag0, type = "lag", n=13)
)]
fit1 = Arima(ts_price_brazilian_naturals[14:336], xreg = lagged_stock_consumption[14:336,1], order = c(0,1,2))
fit2 = Arima(ts_price_brazilian_naturals[14:336], xreg = lagged_stock_consumption[14:336,2], order = c(0,1,2))
fit3 = Arima(ts_price_brazilian_naturals[14:336], xreg = lagged_stock_consumption[14:336,3], order = c(0,1,2))
fit4 = Arima(ts_price_brazilian_naturals[14:336], xreg = lagged_stock_consumption[14:336,4], order = c(0,1,2))
c(fit1[['aicc']], fit2[['aicc']], fit3[['aicc']], fit4[['aicc']])
[1] 2550.643 2551.125 2547.686 2548.305
summary(fit1)
Series: ts_price_brazilian_naturals[14:336] 
Regression with ARIMA(0,1,2) errors 

Coefficients:
         ma1     ma2  StockConsumptionLag0
      0.1356  0.2069               29.5989
s.e.  0.0551  0.0530               35.2218

sigma^2 estimated as 158.7:  log likelihood=-1271.26
AIC=2550.52   AICc=2550.64   BIC=2565.62

Training set error measures:
                     ME     RMSE      MAE        MPE     MAPE      MASE       ACF1
Training set -0.0114022 12.52045 8.268132 -0.2530818 6.039592 0.9815792 0.01242713
summary(fit2)
Series: ts_price_brazilian_naturals[14:336] 
Regression with ARIMA(0,1,2) errors 

Coefficients:
         ma1     ma2  StockConsumptionLag1
      0.1351  0.2097              -16.6144
s.e.  0.0551  0.0531               34.4507

sigma^2 estimated as 159:  log likelihood=-1271.5
AIC=2551   AICc=2551.13   BIC=2566.1

Training set error measures:
                      ME     RMSE      MAE       MPE     MAPE      MASE       ACF1
Training set -0.07268384 12.52978 8.279962 -0.232789 6.032932 0.9829837 0.01303235
summary(fit3)
Series: ts_price_brazilian_naturals[14:336] 
Regression with ARIMA(0,1,2) errors 

Coefficients:
         ma1     ma2  StockConsumptionLag2
      0.1312  0.2025               -65.294
s.e.  0.0551  0.0532                33.897

sigma^2 estimated as 157.3:  log likelihood=-1269.78
AIC=2547.56   AICc=2547.69   BIC=2562.66

Training set error measures:
                     ME     RMSE      MAE        MPE     MAPE      MASE       ACF1
Training set -0.1685919 12.46319 8.260371 -0.1990917 6.033872 0.9806579 0.01121578
summary(fit4)
Series: ts_price_brazilian_naturals[14:336] 
Regression with ARIMA(0,1,2) errors 

Coefficients:
         ma1     ma2  StockConsumptionLag3
      0.1277  0.2046              -59.6471
s.e.  0.0549  0.0538               33.9187

sigma^2 estimated as 157.6:  log likelihood=-1270.09
AIC=2548.18   AICc=2548.3   BIC=2563.28

Training set error measures:
                     ME     RMSE      MAE        MPE     MAPE      MASE       ACF1
Training set -0.1611911 12.47514 8.263506 -0.1941974 6.036215 0.9810301 0.01029604
arimax_forecast = function(ts, h, xreg){
  arima_xreg_fit = Arima(ts, order = c(0,1,2), xreg = xreg[1:length(ts)])
  return(forecast(arima_xreg_fit, h = h, xreg = xreg[(length(ts)+1):(length(ts)+h)]))
}
e = tsCV(
  ts_price_brazilian_naturals[1:336], 
  arimax_forecast,
  h=4, 
  window = 120,
  xreg = ts_stock_to_consumption_monthly_list[["Brazilian Naturals"]])
e^2 %>% mean(na.rm = TRUE) %>% sqrt()
[1] 15.77182
LS0tCnRpdGxlOiAiQ29mZmVlIHByaWNlIG1vZGVsaW5nIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIElDTzogY29mZmVlIHByaWNlcyAgCiMjIEluZGljYXRpdmUgY29mZmVlIHByaWNlcyAgCgpgYGB7ciBsaWJyYXJpZXN9CmxpYnJhcnkoZGF0YS50YWJsZSkgI3RhYnVsYXIgZGF0YSBtYW5pcHVsYXRpb24gbGlicmFyeQpsaWJyYXJ5KGhlcmUpICAgICAgICNwcm9qZWN0IGZpbGUgc3lzdGVtIG9yZ2FuaXphdGlvbmFsIGxpYnJhcnkKbGlicmFyeShnZ3Bsb3QyKSAgICAjcGxvdHRpbmcKbGlicmFyeShrbml0cikgICAgICAjUk1hcmtkb3duIGtuaXR0aW5nIGxpYnJhcnkKbGlicmFyeShncmlkRXh0cmEpICAjQWRkaXRpb25hbCBmdW5jdGlvbnMgZm9yIGdncGxvdDIKbGlicmFyeShrYWJsZUV4dHJhKSAjVGFibGUgc3R5bGluZyBmb3IgUk1hcmtkb3duCmxpYnJhcnkoZnBwMikgICAgICAgI1RpbWUgU2VyaWVzIG1vZGVsaW5nIGxpYnJhcnkKbGlicmFyeShzY2FsZXMpICAgICAjRm9ybWF0dGluZyBmdW5jdGlvbnMKbGlicmFyeSh1cmNhKQpsaWJyYXJ5KHRlbXBkaXNhZ2cpCmxpYnJhcnkodmFycykKCiNvdGhlciB1c2VkIGxpYnJhcmllcyB1cmNhLCBUU0EKYGBgCgoKYGBge3IgZGF0YX0Kc291cmNlKGhlcmU6OmhlcmUoInNjcmlwdHMvZGF0YSBwcm9jZXNzaW5nL2RhdGFfcHJvY2Vzc2luZy5SIikpCnByaWNlcyA9IHJlYWRfaW5kaWNhdGl2ZV9wcmljZXMoKQpncm93ZXJfcHJpY2VzID0gcmVhZF9ncm93ZXJfcHJpY2VzKCkKZXhwb3J0YWJsZV9wcm9kdWN0aW9uID0gcmVhZF9leHBvcnRhYmxlX3Byb2R1Y3Rpb24oKQp0b3RhbF9wcm9kdWN0aW9uID0gcmVhZF90b3RhbF9wcm9kdWN0aW9uKCkKY29uc3VtcHRpb24gPSByZWFkX2NvbnN1bXB0aW9uKCkKaW52ZW50b3JpZXMgPSByZWFkX2ludmVudG9yaWVzKCkKCmNvZmZlZV90eXBlcyA9IHVuaXF1ZSh0b3RhbF9wcm9kdWN0aW9uJENvZmZlZVR5cGUpCgpwbG90X2NvZmZlZV90cyA9IGZ1bmN0aW9uKHRzX2xpc3QsIG1lYXN1cmUpewogIHBsb3RfbGlzdCA9IAogICAgbGFwcGx5KAogICAgICBjb2ZmZWVfdHlwZXMsCiAgICAgIGZ1bmN0aW9uKGNvZmZlZV90eXBlKXsKICAgICAgICBhdXRvcGxvdCh0c19saXN0W1tjb2ZmZWVfdHlwZV1dKSsKICAgICAgICAgIHRoZW1lX21pbmltYWwoKSsKICAgICAgICAgIHhsYWIoIiIpKwogICAgICAgICAgeWxhYihtZWFzdXJlKSsKICAgICAgICAgIGdndGl0bGUoY29mZmVlX3R5cGUpCiAgICAgIH0KICAgICkKICAKICBkby5jYWxsKCJncmlkLmFycmFuZ2UiLCBjKHBsb3RfbGlzdCwgbmNvbCA9IDIpKQp9CmBgYAoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9Nn0KZ2dwbG90KHByaWNlcykrCiAgZ2VvbV9saW5lKGFlcyh4ID0gTW9udGgsIHk9SW5kaWNhdGl2ZV9QcmljZSwgY29sb3I9Q29mZmVlVHlwZSkpKwogIHRoZW1lX21pbmltYWwoKSsKICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMSB5ZWFyIiwgZGF0ZV9sYWJlbHMgPSAiJVkiKSsKICB4bGFiKCIiKSsKICB5bGFiKCJQcmljZSBpbiBVUyBjZW50cy9sYiIpKwogIGdndGl0bGUoIk5vbWluYWwgY29mZmVlIGluZGljYXRpdmUgcHJpY2UgcmVwb3J0ZWQgYnkgSUNPIikKYGBgCkxvb2tpbmcgYXQgdGhlIHByaWNlIGdyYXBoIGZvciB0aGUgcGFzdCAyNyB5ZWFycyBpdCBpcyBvYnZpb3VzIHRoYXQgY29mZmVlIHByaWNlcyBmb3IgZGlmZmVyZW50IHZhcmlldGllcyBhcmUgY2xvc2VseSBjb3JyZWxhdGVkLiBCdXQgcm9idXN0YXMgYXJlIGRpdmVyZ2VkIHNsaWdodGx5IGZyb20gdGhlIGFyYWJpY2FzIGluIHRoZSBwYXN0IDEwIHllYXJzIGFuZCBub3QgaGF2ZSBzdWNoIHN0cm9uZyBzcGlrZXMgaW4gdGhlIHByaWNlcy4gICAgCgpTaW5jZSB3ZSBhcmUgd29ya2luZyBvbiBwcmljZSBkYXRhLCB0byBhdm9pZCBpbmZsYXRpb24gcmVsYXRlZCBpc3N1ZXMgd2Ugc2hvdWxkIG5vcm1hbGl6ZSBwcmljZXMuICAgCldvcmxkIEJhbmsncyBDb25zdW1lciBQcmljZSBJbmRleCBkYXRhIHdhcyB1c2VkIGZvciByZXNjYWxpbmcuICAKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTZ9CmdncGxvdChwcmljZXMpKwogIGdlb21fbGluZShhZXMoeCA9IE1vbnRoLCB5PUNvbnN0YW50MjAxMFByaWNlLCBjb2xvcj1Db2ZmZWVUeXBlKSkrCiAgdGhlbWVfbWluaW1hbCgpKwogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIxIHllYXIiLCBkYXRlX2xhYmVscyA9ICIlWSIpKwogIHhsYWIoIiIpKwogIHlsYWIoIlByaWNlIGluIFVTIGNlbnRzL2xiIikrCiAgZ2d0aXRsZSgiQ29uc3RhbnQgMjAxMCBjb2ZmZWUgaW5kaWNhdGl2ZSBwcmljZSByZXBvcnRlZCBieSBJQ08iKQpgYGAKCkdvaW5nIGZvcndhcmQgYWxsIG1vZGVsaW5nIGFuZCBldmFsdWF0aW9uIG9mIHByaWNlIHdpbGwgYmUgZG9uZSB1c2luZyBDb25zdGFudCAyMDEwIFVTRC4gIAoKQXMgd2l0aCBtYWpvcml0eSBvZiBwcmljZSB0aW1lIHNlcmllcywgdGhleSAKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9Nn0KZ2dBY2YoZGlmZihwcmljZXNbQ29mZmVlVHlwZT09IlJvYnVzdGFzIl0kQ29uc3RhbnQyMDEwUHJpY2UpKQpgYGAKCgpJZiB3ZSBsb29rIGFuZCBDcm9zcy1Db3JyZWxhdGlvbiBiZXR3ZWVuIHByaWNlIHRpbWUgc2VyaWVzIHdlIHdpbGwgc2VlIGFuIGFsbW9zdCAxIGNvcnJlbGF0aW9uIGF0IGxhZyAwIGZvciBtYWpvcml0eSBvZiBhcmFiaWNhcyBhbmQgbmVhciAwLjYtMC44IGNvcnJlbGF0aW9uIG9mIFJvYnVzdGFzIHdpdGggQXJhYmljYXMgdmFyaWF0aW9ucy4gCmBgYHtyfQp0c19wcmljZV9saXN0ID0gCiAgbGFwcGx5KAogICAgY29mZmVlX3R5cGVzLAogICAgZnVuY3Rpb24obmFtZSl7CiAgICAgIHJldHVybigKICAgICAgICB0cyhwcmljZXNbQ29mZmVlVHlwZSA9PSBuYW1lXVtvcmRlcihNb250aCldJENvbnN0YW50MjAxMFByaWNlLAogICAgICAgICAgICAgc3RhcnQgPSBtaW4oeWVhcihwcmljZXMkTW9udGgpKSwgZnJlcXVlbmN5ID0gMTIKICAgICAgICApCiAgICAgICkKICAgIH0KICApCm5hbWVzKHRzX3ByaWNlX2xpc3QpID0gY29mZmVlX3R5cGVzCgpwbG90X2xpc3QgPSBsaXN0KCkKaSA9IDE7CmZvciAoY29mZmVlX3R5cGUgaW4gbmFtZXModHNfcHJpY2VfbGlzdCkpewogIGZvciAoY29mZmVlX3R5cGVfMiBpbiBuYW1lcyh0c19wcmljZV9saXN0KSl7CgogICAgcCA9IGdnQ2NmKHVubGlzdCh0c19wcmljZV9saXN0W1tjb2ZmZWVfdHlwZV1dKSwgdW5saXN0KHRzX3ByaWNlX2xpc3RbW2NvZmZlZV90eXBlXzJdXSkpKwogICAgICB0aGVtZV9taW5pbWFsKCkrCiAgICAgIGdndGl0bGUoIiIpKwogICAgICB4bGFiKCIiKSsKICAgICAgeWxhYigiIikKICAgIAogICAgcGxvdF9saXN0W1tpXV0gPSBwCiAgICBpID0gaSArIDEKICB9Cn0KCmRvLmNhbGwoImdyaWQuYXJyYW5nZSIsIGMocGxvdF9saXN0LCBuY29sID0gNCwgdG9wID0gIlJvYnVzdGFzLCBCcmF6aWxpYW4gTmF0dXJhbHMsIE90aGVyIE1pbGRzLCBDb2xvbWJpYW4gTWlsZHMiKSkKYGBgCgojIyBQcmljZXMgcGFpZCB0byBncm93ZXJzICAgIApTaW1pbGFybHkgdG8gaW5kaWNhdGl2ZSBwcmljZXMsIHdlIHJlc2NhbGVkIHByaWNlcyBwYWlkIHRvIGdyb3dlcnMgdG8gQ29uc3RhbnQgMjAxMCBVU0QuICAKUHJpY2VzIHBhaWQgdG8gZ3Jvd2VycyB2YXJ5IHNpZ25pZmljYW50bHkgYWNyb3NzIGV4cG9ydGVyIGNvdW50cmllcy4gIApgYGB7ciByZXN1bHRzPSdhc2lzJ30KZ3Jvd2VyX3ByaWNlc1tZZWFyID09IDIwMTZdWywgLigKICBNaW5QcmljZSA9IG1pbihQcmljZSksIAogIEF2Z1ByaWNlID0gbWVhbihQcmljZSksIAogIFByaWNlU2QgPSBzZChQcmljZSksCiAgTWF4UHJpY2UgPSBtYXgoUHJpY2UpCiksIGJ5PUNvZmZlZVR5cGVdJT4lCiAga2FibGUoZm9ybWF0PSJodG1sIiwgY29sLm5hbWVzID0gYygiQ29mZmVlIiwiTWluIiwgIkF2ZyIsICJTRCIsICJNYXgiKSklPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIpLCBmdWxsX3dpZHRoID0gRkFMU0UpJT4lCiAgYWRkX2hlYWRlcl9hYm92ZShoZWFkZXIgPSBjKCIgIj0xLCAiUHJpY2UiID0gNCkpCmBgYAoKClByaWNlcyBwYWlkIHRvIGdyb3dlcnMgYXJlIGF2YWlsYWJsZSBvbiB5ZWFybHkgYXZlcmFnZSBsZXZlbCwgYnV0IGV2ZW4gb24gdGhpcyBsZXZlbCB3ZSBzZWUgdGhhdCBncm93ZXIgcHJpY2VzIHN0cm9uZ2x5IGNvcnJlbGF0ZSB3aXRoIGludGVybmF0aW9uYWwgZXhwb3J0IHByaWNlcyBhbmQgaXQgaXMgYSBxdWVzdGlvbiB3aGV0aGVyIGhpZ2ggZ3Jvd2VyIHByaWNlcyBwdXNoIHRoZSBpbnRlcm5hdGlvbmFsIHByaWNlcyB1cCBvciBoaWdoZXIgcHJvZml0IG1hcmdpbnMgb24gaW50ZXJuYXRpb25hbCB0cmFkZSBhbGxvd3MgZm9yIGhpZ2hlciBncm93ZXIgcHJpY2VzIHRvIGJlIHBhaWQgbG9jYWxseS4gICAKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTZ9CnNjYWxlZF9ncm93ZXJfcHJpY2VzID0gbWVyZ2UoCiAgZ3Jvd2VyX3ByaWNlcywgdG90YWxfcHJvZHVjdGlvbiwKICBieS54ID0gYygiQ291bnRyeSIsICJDb2ZmZWVUeXBlIiwgIlllYXIiKSwgYnkueSA9IGMoIkNvdW50cnkiLCAiQ29mZmVlVHlwZSIsICJZZWFyIiksCiAgYWxsLnggPSBUUlVFLCBhbGwueSA9IEZBTFNFCikKCnNjYWxlZF9ncm93ZXJfcHJpY2VzID0gCiAgc2NhbGVkX2dyb3dlcl9wcmljZXNbLC4oCiAgICBQcmljZSA9IHN1bShDb25zdGFudDIwMTBQcmljZSAqIFRvdGFsUHJvZHVjdGlvbikvc3VtKFRvdGFsUHJvZHVjdGlvbikKICApLCBieSA9IC4oQ29mZmVlVHlwZSwgWWVhcildCgp0c19ncm93ZXJfcHJpY2VzID0gCiAgbGFwcGx5KAogICAgY29mZmVlX3R5cGVzLAogICAgZnVuY3Rpb24obmFtZSl7CiAgICAgIHJldHVybih0cyhzY2FsZWRfZ3Jvd2VyX3ByaWNlc1tDb2ZmZWVUeXBlID09IG5hbWVdW29yZGVyKFllYXIpXSRQcmljZSwgCiAgICAgICAgICAgICAgICBzdGFydCA9IG1pbihzY2FsZWRfZ3Jvd2VyX3ByaWNlcyRZZWFyKSkpCiAgICB9CiAgKQpuYW1lcyh0c19ncm93ZXJfcHJpY2VzKSA9IGNvZmZlZV90eXBlcwoKdHNfZ3Jvd2VyX3ByaWNlc19tb250aGx5ID0gCiAgbGFwcGx5KAogICAgY29mZmVlX3R5cGVzLCAKICAgIGZ1bmN0aW9uKG5hbWUpewogICAgICB0c3MgPSB0c19ncm93ZXJfcHJpY2VzW1tuYW1lXV0KICAgICAgdGRhID0gdGQodHNzfjEsIGNvbnZlcnNpb249ImF2ZXJhZ2UiLCB0byA9ICJtb250aGx5IiwgbWV0aG9kPSJkZW50b24tY2hvbGV0dGUiKQogICAgICByZXR1cm4ocHJlZGljdCh0ZGEpKQogICAgfQogICkKbmFtZXModHNfZ3Jvd2VyX3ByaWNlc19tb250aGx5KSA9IGNvZmZlZV90eXBlcwpnZ3Bsb3Qoc2NhbGVkX2dyb3dlcl9wcmljZXMpKwogIGdlb21fbGluZShhZXMoeCA9IFllYXIsIHk9UHJpY2UsIGNvbG9yPUNvZmZlZVR5cGUpKSsKICB0aGVtZV9taW5pbWFsKCkrCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShtaW4oc2NhbGVkX2dyb3dlcl9wcmljZXMkWWVhciksIG1heChzY2FsZWRfZ3Jvd2VyX3ByaWNlcyRZZWFyKSkpKwogIHhsYWIoIiIpKwogIHlsYWIoIlByaWNlIGluIFVTIGNlbnRzL2xiIikrCiAgZ2d0aXRsZSgiVG90YWwgcHJvZHVjdGlvbiB3ZWlnaHRlZCBncm93ZXJzIHByaWNlIGZvciBjb2ZmZWUgdHlwZXMiKQpgYGAKCiMjIFByb2R1Y3Rpb24gIApDZXJ0YWlubHkgY29mZmVlIHByaWNlcyBhcmUgYWxzbyBkZXBlbmRlbnQgb24gc3VwcGx5IHNpZGUgb2YgdGhlIG1hcmtldCBkeW5hbWljcy4gT3ZlciB0aGUgcGFzdCAyNyB5ZWFycyBwcm9kdWN0aW9uIG9mIEJyYXppbGlhbiBOYXR1cmFscyBhbmQgUm9idXN0YXMgYWxtb3N0IHRyaXBwbGVkLCB3aGlsZSBwcm9kdWN0aW9uIG9mIENvbG9tYmlhbiBNaWxkcyBoYXZlIHN0YWduYXRlZC4gIAoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9Nn0KcHJvZHVjdGlvbl95ZWFyX2NvZmZlZSA9IHRvdGFsX3Byb2R1Y3Rpb25bLC4oVG90YWxQcm9kdWN0aW9uID0gc3VtKFRvdGFsUHJvZHVjdGlvbikpLCBieT0uKFllYXIsIENvZmZlZVR5cGUpXQpleHBvcnRhYmxlX3Byb2R1Y3Rpb25feWVhcl9jb2ZmZWUgPSBleHBvcnRhYmxlX3Byb2R1Y3Rpb25bLC4oVG90YWxQcm9kdWN0aW9uID0gc3VtKFRvdGFsUHJvZHVjdGlvbikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnk9LihZZWFyLENvZmZlZVR5cGUpXQpnZ3Bsb3QocHJvZHVjdGlvbl95ZWFyX2NvZmZlZSkrCiAgZ2VvbV9saW5lKGFlcyh4PVllYXIsIHkgPSBUb3RhbFByb2R1Y3Rpb24sIGdyb3VwPUNvZmZlZVR5cGUsIGNvbG9yPUNvZmZlZVR5cGUpKSsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpKwogIHlsYWIoIlRvdGFsIHByb2R1Y3Rpb24gaW4gdGhvdXNhbmRzIDYwa2cgYmFncyIpKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCiMjIENvbnN1bXB0aW9uICAgCkZyb20gdGhlIGRlbWFuZCBzaWRlIElDTyByZXBvcnRzIHRoZSBmb2xsb3dpbmcgZHluYW1pY3MuICAKSXQgYXBwZWFycyB0aGF0IGNvbnN1bXB0aW9uIGluY3JlYXNlcyBhbG1vc3QgbGluZWFybHkgd2l0aCB2ZXJ5IG1pbm9yIHZhcmlhdGlvbiBkdWUgdG8gZWNvbm9taWMgZmFjdG9ycy4gSnVkZ2luZyBieSB0aGUgZ3JhcGhzIGl0IG1pZ2h0IG5vdCBiZSBuZWNlc3NhcnkgdG8gbW9kZWwgY29uc3VtcHRpb24gb2YgYSBwYXJ0aWN1bGFyIGNvZmZlZSB0eXBlIGFzIGEgZnVuY3Rpb24gb2Ygd29ybGQgR0RQLiAgCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD02fQpjb25zdW1wdGlvbl95ZWFyX2NvZmZlZSA9IGNvbnN1bXB0aW9uWywgLihDb25zdW1wdGlvbiA9IHN1bShDb25zdW1wdGlvbikpLCBieT0uKFllYXIsIENvZmZlZVR5cGUpXQoKZ2dwbG90KGNvbnN1bXB0aW9uX3llYXJfY29mZmVlKSsKICBnZW9tX2xpbmUoYWVzKHg9WWVhciwgeT1Db25zdW1wdGlvbiwgZ3JvdXA9Q29mZmVlVHlwZSwgY29sb3I9Q29mZmVlVHlwZSkpKwogIHhsYWIoIiIpKwogIHlsYWIoIkNvbnN1bXB0aW9uIGluIHRob3VzYW5kIDYwa2cgYmFncyIpKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkrCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShtaW4oY29uc3VtcHRpb25feWVhcl9jb2ZmZWUkWWVhciksIG1heChjb25zdW1wdGlvbl95ZWFyX2NvZmZlZSRZZWFyKSkpKwogIGdndGl0bGUoIldvcmxkd2lkZSBjb25zdW1wdGlvbiBvZiBjb2ZmZWUgdHlwZXMiKSsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD02fQp0bXAgPSBjb25zdW1wdGlvblssIC4oQ29uc3VtcHRpb24gPSBzdW0oQ29uc3VtcHRpb24pKSwgYnk9LihZZWFyLCBDb3VudHJ5KV0KCmdncGxvdCh0bXApKwogIGdlb21fbGluZShhZXMoeD1ZZWFyLCB5PUNvbnN1bXB0aW9uLCBncm91cD1Db3VudHJ5LCBjb2xvcj1Db3VudHJ5KSkrCiAgeGxhYigiIikrCiAgeWxhYigiQ29uc3VtcHRpb24gaW4gdGhvdXNhbmQgNjBrZyBiYWdzIikrCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKG1pbih0bXAkWWVhciksIG1heCh0bXAkWWVhcikpKSsKICBnZ3RpdGxlKCJXb3JsZHdpZGUgY29uc3VtcHRpb24gb2YgY29mZmVlIHR5cGVzIikrCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKIyMgSW52ZW50b3JpZXMgIApJQ08gcmVwb3J0cyBlbmQgb2YgdGhlIHllYXIgaW52ZW50b3JpZXMgYXQgaW1wb3J0ZXIgY291bnRyaWVzLiAgIApJZiB3ZSBhZ2dyZWdhdGUgYnkgY29mZmVlIHR5cGUgd2UgZ2V0IHRoZSBmb2xsb3dpbmcgYXZhaWxhYmxlIGludmVudG9yaWVzLiAgCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTh9CmludmVudG9yaWVzX3llYXJfY29mZmVlID0gaW52ZW50b3JpZXNbLCAuKEludmVudG9yeSA9IHN1bShJbnZlbnRvcnkpKSwgYnk9LihZZWFyLCBDb2ZmZWVUeXBlKV0KCmdncGxvdChpbnZlbnRvcmllc195ZWFyX2NvZmZlZSkrCiAgZ2VvbV9saW5lKGFlcyh4PVllYXIsIHk9SW52ZW50b3J5LCBncm91cD1Db2ZmZWVUeXBlLCBjb2xvcj1Db2ZmZWVUeXBlKSkrCiAgeGxhYigiIikrCiAgeWxhYigiSW52ZW50b3J5IGluIHRob3VzYW5kIDYwa2cgYmFncyIpKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkrCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShtaW4oaW52ZW50b3JpZXNfeWVhcl9jb2ZmZWUkWWVhciksIG1heChpbnZlbnRvcmllc195ZWFyX2NvZmZlZSRZZWFyKSkpKwogIGdndGl0bGUoIldvcmxkd2lkZSBpbnZlbnRvcnkgb2YgY29mZmVlIHR5cGVzIikrCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKICAKIyBQcmVkaWN0aW5nIGNvZmZlZSBwcmljZSAgIAoKIyMgUHJpY2Ugc2Vhc29uYWxpdHkgYW5kIHRyZW5kICAKVG8gZGVjb21wb3NlIHByaWNlIHRpbWUgc2VyaWVzIGludG8gdHJlbmQgYW5kIHNlYXNvbmFsIGNvbXBvbmVudHMgd2Ugd2lsbCB1c2UgU1RMIG1ldGhvZCwgd2hpY2ggc3RhbmRzIGZvciAiU2Vhc29uYWwgYW5kIFRyZW5kIGRlY29tcG9zaXRpb24gdXNpbmcgTG9lc3MiLiAgCgpgYGB7ciBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD02fQpwbG90X2xpc3QgPSBsaXN0KCkKCmZvciAobmFtZSBpbiBuYW1lcyh0c19wcmljZV9saXN0KSl7CiAgcGxvdF9saXN0W1tuYW1lXV0gPSAKICAgIHRzX3ByaWNlX2xpc3RbW25hbWVdXSAlPiUKICAgIHN0bCh0LndpbmRvdyA9IDEzLCBzLndpbmRvdyA9ICJwZXJpb2RpYyIsIHJvYnVzdD1UUlVFKSU+JQogICAgYXV0b3Bsb3QoKSsKICAgIGdndGl0bGUobmFtZSkrCiAgICB0aGVtZV9taW5pbWFsKCkgICAgCn0KCmRvLmNhbGwoImdyaWQuYXJyYW5nZSIsIGMocGxvdF9saXN0LCBuY29sID0gMikpCmBgYAoKRnJvbSB0aGUgZ3JhcGhzIGl0IGlzIGFwcGFyZW50IHRoYXQgdGhlIHNlYXNvbmFsIGNvbXBvbmVudCBpcyBub3QgdmVyeSBzdHJvbmcgaW4gY29mZmVlIHByaWNlIHRpbWUgc2VyaWVzLiBUaGlzIG9ic2VydmF0aW9uIGNhbiBiZSB2YWxpZGF0ZWQgYnkgbWVhc3VyaW5nIHN0cmVuZ3RoIG9mIHRyZW5kL3NlYXNvbmFsIGNvbXBvbmVudHMgaW4gcmVsYXRpb24gdG8gcmVzaWR1YWwuICAKJCQKRl97dH09bWF4KDAsMS1cZnJhY3tWYXIoUl90KX17VmFyKFRfdCtSX3QpfSkKJCQKd2hlcmUgJEZfdCQgLSBzdHJlbmd0aCBvZiB0cmVuZCwgJFJfdCQgcmVzaWR1YWxzIGFuZCAkVF90JCBpcyB0cmVuZC4gIAoKYGBge3IgcmVzdWx0cz0nYXNpcyd9CnJvYnVzdGFfc3RsID0gc3RsKHRzX3ByaWNlX2xpc3RbWyJSb2J1c3RhcyJdXSwgdC53aW5kb3cgPSAxMywgcy53aW5kb3cgPSAicGVyaW9kaWMiLCByb2J1c3Q9VFJVRSkKCnRyZW5kX3N0cmVuZ3RoID0gZnVuY3Rpb24oc3RsX3Jlc3VsdCl7CiAgcmV0dXJuKAogICAgbWF4KAogICAgICAgIDAsIAogICAgICAgIDEgLSB2YXIoc3RsX3Jlc3VsdCR0aW1lLnNlcmllc1ssInJlbWFpbmRlciJdKS8KICAgICAgICAgICAgICB2YXIoc3RsX3Jlc3VsdCR0aW1lLnNlcmllc1ssInRyZW5kIl0rc3RsX3Jlc3VsdCR0aW1lLnNlcmllc1ssInJlbWFpbmRlciJdKQogICAgKQogICkKfQoKc2Vhc29uYWxfc3RyZW5ndGggPSBmdW5jdGlvbihzdGxfcmVzdWx0KXsKICByZXR1cm4oCiAgICBtYXgoCiAgICAgICAgMCwgCiAgICAgICAgMSAtIHZhcihzdGxfcmVzdWx0JHRpbWUuc2VyaWVzWywicmVtYWluZGVyIl0pLwogICAgICAgICAgICAgIHZhcihzdGxfcmVzdWx0JHRpbWUuc2VyaWVzWywic2Vhc29uYWwiXStzdGxfcmVzdWx0JHRpbWUuc2VyaWVzWywicmVtYWluZGVyIl0pCiAgICApCiAgKQp9CgpyYmluZGxpc3QoCmxhcHBseShjb2ZmZWVfdHlwZXMsIAogICAgICAgIGZ1bmN0aW9uKG5hbWUpewogIHN0bF9yZXN1bHQgPSBzdGwodHNfcHJpY2VfbGlzdFtbbmFtZV1dLCB0LndpbmRvdyA9IDEzLCBzLndpbmRvdyA9ICJwZXJpb2RpYyIsIHJvYnVzdD1UUlVFKQogIHJldHVybihsaXN0KAogICAgQ29mZmVlVHlwZSA9IG5hbWUsCiAgICBUcmVuZFN0cmVuZ3RoID0gdHJlbmRfc3RyZW5ndGgoc3RsX3Jlc3VsdCksCiAgICBTZWFzb25hbFN0cmVuZ3RoID0gc2Vhc29uYWxfc3RyZW5ndGgoc3RsX3Jlc3VsdCkKICApKQp9KSklPiUKICBrYWJsZShmb3JtYXQ9Imh0bWwiLCBjb2wubmFtZXMgPSBjKCJDb2ZmZWUgVHlwZSIsICJUcmVuZCBzdHJlbmd0aCIsICJTZWFzb25hbCBzdHJlbmd0aCIpKSU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkKYGBgCgpUaGUgc2Vhc29uYWxpdHkgc3RyZW5ndGggaXMgbWluaW1hbCBpbiBhbGwgcHJpY2UgdGltZSBzZXJpZXMsIGFzIGEgcmVzdWx0IG1vc3Qgb2YgZGlzYWdyZWdhdGlvbnMgZm9yIG90aGVyIHRpbWUgc2VyaWVzIGNhbiBpZ25vcmUgc2Vhc29uYWxpdHkgYW5kIGJlIGRvbmUgdXNpbmcgbGluZWFyIGludGVycG9sYXRpb25zLiAgCgojIyBEaXNhZ2dyZWdhdGluZyB5ZWFybHkgZGF0YSB0byBtb250aGx5IGxldmVsICAKU2luY2Ugb3VyIHByaWNlIGRhdGEgaXMgb24gYSBtb250aGx5IGxldmVsIGl0IGlzIHByZWZlcmFibGUgdG8gbW9kZWwgdGhlIHByaWNlcyBvbiB0aGUgc2FtZSBsZXZlbC4gT24gYW5vdGhlciBoYW5kIG1ham9yaXR5IG9mIG91ciBwcm9kdWN0aW9uIGFuZCBjb25zdW1wdGlvbiBkYXRhIGlzIG9uIGEgeWVhcmx5IGFnZ3JlZ2F0ZWQgbGV2ZWwuICAKVGhlcmUgaXMgYSB2YXJpZXR5IG9mIHN0YXRpc3RpY2FsIG1ldGhvZHMgdG8gZGlzYWdncmVnYXRlIHllYXJseSBkYXRhIHRvIG1vbnRobHkgZGF0YSwgZGVwZW5kaW5nIG9uIGEgc2Vhc29uYWxpdHkgYXNzdW1wdGlvbnMuICAKClRyZW5kLXNlYXNvbmFsIGRlY29tcG9zaXRpb24gb2YgcHJpY2UgdGltZSBzZXJpZXMgc2hvd2VkIHRoYXQgcHJpY2UgaXMgZG9lcyBub3QgaGF2ZSBhIHN0cm9uZyBzZWFzb25hbCBjb21wb25lbnQgYXMgYSByZXN1bHQgd2Ugd2lsbCB1c2UgdGhlIGJhc2ljIGRpc2FnZ3JlZ2F0aW9uIG1vZGVsLiAKCiMjIyBQcmljZSB0byBncm93ZXJzICAKCgoKIyMjIENvbnN1bXB0aW9uICAKSXQgaXMgZmFpciB0byBhc3N1bWUgdGhhdCBjb25zdW1wdGlvbiBvZiBjb2ZmZWUgZG9lc24ndCBoYXZlIHN0cm9uZyBzZWFzb25hbCBjaGFyYWN0ZXJpc3RpY3MgYXMgc3VjaCBkaXNhZ2dyZWdhdGlvbiBtb2RlbCBkb2VzIG5vdCByZXF1aXJlIHNlYXNvbmFsIGluZGljYXRvciBzZXJpZXMuICAKCmBgYHtyfQp0c19jb25zdW1wdGlvbl9saXN0ID0gCiAgbGFwcGx5KAogICAgY29mZmVlX3R5cGVzLAogICAgZnVuY3Rpb24oY29mZmVlX3R5cGUpewogICAgICByZXR1cm4oCiAgICAgICAgdHMoY29uc3VtcHRpb25feWVhcl9jb2ZmZWVbQ29mZmVlVHlwZSA9PSBjb2ZmZWVfdHlwZV1bb3JkZXIoWWVhcildJENvbnN1bXB0aW9uLAogICAgICAgICAgIHN0YXJ0ID0gbWluKGNvbnN1bXB0aW9uX3llYXJfY29mZmVlJFllYXIpKQogICAgICApCiAgICB9CiAgKQpuYW1lcyh0c19jb25zdW1wdGlvbl9saXN0KSA9IGNvZmZlZV90eXBlcwoKdHNfY29uc3VtcHRpb25fbW9udGhseV9saXN0ID0gCiAgbGFwcGx5KAogICAgY29mZmVlX3R5cGVzLAogICAgZnVuY3Rpb24oY29mZmVlX3R5cGUpewogICAgICB0c2QgPSB0c19jb25zdW1wdGlvbl9saXN0W1tjb2ZmZWVfdHlwZV1dCiAgICAgIHJldHVybihwcmVkaWN0KHRkKHRzZCB+IDEsIHRvID0gIm1vbnRobHkiLCBtZXRob2QgPSAiZGVudG9uLWNob2xldHRlIiwgY29udmVyc2lvbiA9ICJzdW0iKSkpCiAgICB9CiAgKQpuYW1lcyh0c19jb25zdW1wdGlvbl9tb250aGx5X2xpc3QpID0gY29mZmVlX3R5cGVzCgpwbG90X2xpc3QgPSAKICBsYXBwbHkoCiAgICBjb2ZmZWVfdHlwZXMsCiAgICBmdW5jdGlvbihjb2ZmZWVfdHlwZSl7CiAgICAgIGF1dG9wbG90KHRzX2NvbnN1bXB0aW9uX21vbnRobHlfbGlzdFtbY29mZmVlX3R5cGVdXSkrCiAgICAgICAgdGhlbWVfbWluaW1hbCgpKwogICAgICAgIHhsYWIoIiIpKwogICAgICAgIHlsYWIoIkNvbnN1bXB0aW9uIikrCiAgICAgICAgZ2d0aXRsZShjb2ZmZWVfdHlwZSkKICAgIH0KICApCgpkby5jYWxsKCJncmlkLmFycmFuZ2UiLCBjKHBsb3RfbGlzdCwgbmNvbCA9IDIpKQpgYGAKCgojIyMgRXhwb3J0YWJsZSBwcm9kdWN0aW9uICAKYGBge3J9Cgp0c19wcm9kdWN0aW9uX2xpc3QgPSAKICBsYXBwbHkoCiAgICBjb2ZmZWVfdHlwZXMsCiAgICBmdW5jdGlvbihjb2ZmZWVfdHlwZSl7CiAgICAgIHJldHVybigKICAgICAgICB0cyhleHBvcnRhYmxlX3Byb2R1Y3Rpb25feWVhcl9jb2ZmZWVbQ29mZmVlVHlwZSA9PSBjb2ZmZWVfdHlwZV1bb3JkZXIoWWVhcildJFRvdGFsUHJvZHVjdGlvbiwKICAgICAgICAgc3RhcnQgPSBtaW4ocHJvZHVjdGlvbl95ZWFyX2NvZmZlZSRZZWFyKQogICAgICAgICkKICAgICAgKQogICAgfQopCm5hbWVzKHRzX3Byb2R1Y3Rpb25fbGlzdCkgPSBjb2ZmZWVfdHlwZXMKCnRzX3Byb2R1Y3Rpb25fbW9udGhseV9saXN0ID0gCiAgbGFwcGx5KAogICAgY29mZmVlX3R5cGVzLAogICAgZnVuY3Rpb24oY29mZmVlX3R5cGUpewogICAgICB0c2QgPSB0c19wcm9kdWN0aW9uX2xpc3RbW2NvZmZlZV90eXBlXV0KICAgICAgcmV0dXJuKHByZWRpY3QodGQodHNkIH4gMSwgdG8gPSAibW9udGhseSIsIG1ldGhvZCA9ICJkZW50b24tY2hvbGV0dGUiLCBjb252ZXJzaW9uID0gInN1bSIpKSkKICAgIH0KICApCm5hbWVzKHRzX3Byb2R1Y3Rpb25fbW9udGhseV9saXN0KSA9IGNvZmZlZV90eXBlcwoKcGxvdF9saXN0ID0gCiAgbGFwcGx5KAogICAgY29mZmVlX3R5cGVzLAogICAgZnVuY3Rpb24oY29mZmVlX3R5cGUpewogICAgICBhdXRvcGxvdCh0c19wcm9kdWN0aW9uX21vbnRobHlfbGlzdFtbY29mZmVlX3R5cGVdXSkrCiAgICAgICAgdGhlbWVfbWluaW1hbCgpKwogICAgICAgIHhsYWIoIiIpKwogICAgICAgIHlsYWIoIlByb2R1Y3Rpb24iKSsKICAgICAgICBnZ3RpdGxlKGNvZmZlZV90eXBlKQogICAgfQogICkKCmRvLmNhbGwoImdyaWQuYXJyYW5nZSIsIGMocGxvdF9saXN0LCBuY29sID0gMikpCmBgYAoKIyMgU3RvY2tzICsgUHJvZHVjdGlvbiAtIENvbnN1bXB0aW9uICAKQSB1c2VmdWwgZGVyaXZlZCBtZWFzdXJlLCB0aGF0IGNvdWxkIGJlIHByZWRpY3RpdmUgb2YgcHJpY2UgZmx1Y3R1YXRpb25zIHdvdWxkIGJlIGF2YWlsYWJsZSBpbnZlbnRvcnkgb2YgY29mZmVlIGF0IHBhcnRpY3VsYXIgbW9udGguICAKSUNPIHJlcG9ydHMgb24gaW52ZW50b3JpZXMgYXQgdGhlIGVuZCBvZiB0aGUgeWVhciBpbiBpbXBvcnRlciBjb3VudHJpZXMsIHdlIGNhbiBkaXNhZ3JlZ2F0ZSBlbmQgb2YgdGhlIHllYXIgc3RvY2tzIGJ5IGluZGljYXRpdmUgdGltZSBzZXJpZXMgb2YgY3VtdWxhdGl2ZSBwcm9kdWN0aW9uIC0gY29uc3VtcHRpb24uIApBIHByZWRpY3RvciB0aGVuIHdvdWxkIGJlIGEgcmF0aW8gYmV0d2VlbiBhdmFpbGFibGUgc3RvY2sgYW5kIHByZWRpY3RlZCB5ZWFybHkgY29uc3VtcHRpb24uCgojIyMgUHJvZHVjdGlvbiAtIENvbnN1bXB0aW9uIGN1bXVsYXRpdmVseSB3aXRoaW4gYSB5ZWFyICAKRGlmZmVyZW5jZSBiZXR3ZWVuIHByb2R1dGlvbiBhbmQgY29uc3VtcHRpb24gKFN0b2NrIGRlbHRhKTogICAgCmBgYHtyfQp0c19zdG9ja19kZWx0YV9saXN0ID0gCiAgbGFwcGx5KAogICAgY29mZmVlX3R5cGVzLCAKICAgIGZ1bmN0aW9uKG5hbWUpewogICAgICByZXR1cm4odHNfcHJvZHVjdGlvbl9tb250aGx5X2xpc3RbW25hbWVdXSAtIHRzX2NvbnN1bXB0aW9uX21vbnRobHlfbGlzdFtbbmFtZV1dKQogICAgfQogICkKbmFtZXModHNfc3RvY2tfZGVsdGFfbGlzdCkgPSBjb2ZmZWVfdHlwZXMKCnBsb3RfbGlzdCA9IAogIGxhcHBseSgKICAgIGNvZmZlZV90eXBlcywKICAgIGZ1bmN0aW9uKGNvZmZlZV90eXBlKXsKICAgICAgYXV0b3Bsb3QodHNfc3RvY2tfZGVsdGFfbGlzdFtbY29mZmVlX3R5cGVdXSkrCiAgICAgICAgdGhlbWVfbWluaW1hbCgpKwogICAgICAgIHhsYWIoIiIpKwogICAgICAgIHlsYWIoIlN0b2NrIGRlbHRhIikrCiAgICAgICAgZ2d0aXRsZShjb2ZmZWVfdHlwZSkKICAgIH0KICApCgpkby5jYWxsKCJncmlkLmFycmFuZ2UiLCBjKHBsb3RfbGlzdCwgbmNvbCA9IDIpKQpgYGAKCk1vbnRobHkgc3RvY2sgZXN0aW1hdGVzICAKCmBgYHtyfQpjdW1zdW1fYnlfeWVhcnMgPSBmdW5jdGlvbih0cyl7CiAgbW9udGhzID0gZmFjdG9yKGN5Y2xlKHRzKSwgbGV2ZWxzPTE6MTIpCiAgbSA9IHRhcHBseSh0cywgbGlzdCh5ZWFyID0gZmxvb3IodGltZSh0cykpLCBtb250aD1tb250aHMpLCBjKQogIG1fc3VtID0gdChhcHBseShtLCAxLCBjdW1zdW0pKQogICNtX3N1bSA9IHQoYXBwbHkobV9zdW0sIDEsIHNjYWxlKSkKICByZXR1cm4odHMoYyh0KG1fc3VtKSksIHN0YXJ0ID0gMTk5MCwgZnJlcXVlbmN5ID0gMTIpKQp9Cgp0c19zdG9ja19kZWx0YV9jdW1fbGlzdCA9IAogIGxhcHBseSgKICAgIGNvZmZlZV90eXBlcywgCiAgICBmdW5jdGlvbihuYW1lKXsKICAgICAgY3Vtc3VtX2J5X3llYXJzKHRzX3N0b2NrX2RlbHRhX2xpc3RbW25hbWVdXSkKICAgIH0KICApCm5hbWVzKHRzX3N0b2NrX2RlbHRhX2N1bV9saXN0KSA9IGNvZmZlZV90eXBlcwoKdHNfc3RvY2tfbGlzdCA9IAogIGxhcHBseSgKICAgIGNvZmZlZV90eXBlcywKICAgIGZ1bmN0aW9uKGNvZmZlZV90eXBlKXsKICAgICAgcmV0dXJuKAogICAgICAgIHRzKGludmVudG9yaWVzX3llYXJfY29mZmVlW0NvZmZlZVR5cGUgPT0gY29mZmVlX3R5cGVdW29yZGVyKFllYXIpXSRJbnZlbnRvcnksCiAgICAgICAgIHN0YXJ0ID0gbWluKGludmVudG9yaWVzX3llYXJfY29mZmVlJFllYXIpCiAgICAgICAgKQogICAgICApCiAgICB9CikKbmFtZXModHNfc3RvY2tfbGlzdCkgPSBjb2ZmZWVfdHlwZXMKCnRzX3N0b2NrX21vbnRobHlfbGlzdCA9IAogIGxhcHBseSgKICAgIGNvZmZlZV90eXBlcywKICAgIGZ1bmN0aW9uKGNvZmZlZV90eXBlKXsKICAgICAgdHNkID0gdHNfc3RvY2tfbGlzdFtbY29mZmVlX3R5cGVdXQogICAgICBzdG9ja19kZWx0YSA9IHRzX3N0b2NrX2RlbHRhX2N1bV9saXN0W1tjb2ZmZWVfdHlwZV1dCiAgICAgIHJldHVybihwcmVkaWN0KHRkKHRzZCB+IDEsIAogICAgICAgICAgICAgICAgICAgICAgICB0byA9ICJtb250aGx5IiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnZlcnNpb24gPSAibGFzdCIsIAogICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiZGVudG9uLWNob2xldHRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgY3JpdGVyaW9uID0gInByb3BvcnRpb25hbCIpKSkKICAgIH0KICApCm5hbWVzKHRzX3N0b2NrX21vbnRobHlfbGlzdCkgPSBjb2ZmZWVfdHlwZXMKCnBsb3RfY29mZmVlX3RzKHRzX3N0b2NrX21vbnRobHlfbGlzdCwgIlN0b2NrIG1vbnRobHkiKQpgYGAKCiMjIyBTdG9jayB0byBjb25zdW1wdGlvbiByYXRpbyAgCmBgYHtyfQp0c19zdG9ja190b19jb25zdW1wdGlvbl9tb250aGx5X2xpc3QgPSAKICBsYXBwbHkoCiAgICBjb2ZmZWVfdHlwZXMsCiAgICBmdW5jdGlvbihjb2ZmZWVfdHlwZSl7CiAgICAgIHJldHVybigKICAgICAgICB0c19zdG9ja19tb250aGx5X2xpc3RbW2NvZmZlZV90eXBlXV0gLyB0c19jb25zdW1wdGlvbl9tb250aGx5X2xpc3RbW2NvZmZlZV90eXBlXV0KICAgICAgKQogICAgfQopCm5hbWVzKHRzX3N0b2NrX3RvX2NvbnN1bXB0aW9uX21vbnRobHlfbGlzdCkgPSBjb2ZmZWVfdHlwZXMKCnBsb3RfY29mZmVlX3RzKHRzX3N0b2NrX3RvX2NvbnN1bXB0aW9uX21vbnRobHlfbGlzdCwgIlN0b2NrL0NvbnN1bXB0aW9uIikKYGBgCgoKIyBQcmljZSBtb2RlbHMgIAoKV2Ugd2lsbCBzdGFydCBtb2RlbGluZyBjb2ZmZWUgcHJpY2Ugd2l0aCB0aGUgc2ltcGxlc3QgbW9kZWxzIHRvIHNldCBhIGJlbmNobWFyayBhbmQgdGhlbiB3aWxsIHNsb3dseSBhZGQgc2Vhc29uYWxpdHkgYW5kIGNvdmFyaWF0ZSBjb21wbGV4aXR5IHdoaWxlIGNvbnRyb2xpbmcgcHJlZGljdGl2ZSBhY2N1cmFjeS4gIAoKIyMgQWNjdXJhY3kgIAoKRm9yIHRpbWUgc2VyaWVzIGZvcmVjYXN0aW5nIGFjY3VyYWN5IHRlc3Rpbmcgd2Ugd2lsbCB1c2UgY3Jvc3MtdmFsaWRhdGlvbiBwcm9jZWR1cmUgb24gcm9sbGluZyBmb3JlY2FzdGluZyBvcmlnaW4gd2l0aCB3aW5kb3cgb2YgNCAod2lsbCBiZSBwcmVkaWN0aW5nIDQgbW9udGhzIGFoZWFkKS4gIApBY2N1cmFjeSB3aWxsIGJlIG1lYXN1cmVkIHVzaW5nIFJNU0UuICAKIVtvcHRpb25hbCBjYXB0aW9uIHRleHRdKGltYWdlcy90c19jdi5wbmcpCgoKIyMgQVJJTUEgIApMZXQncyBzdGFydCBieSBtb2RlbGluZyBjb2ZmZWUgcHJpY2VzIGFzIHNlcGFyYXRlIHRpbWUgc2VyaWVzIHVzaW5nIEFSSU1BIHdpdGhvdXQgYW55IGNvdmFyaWF0ZXMuICAKRmlyc3Qgd2UgbmVlZCB0byBjaGVjayBpZiBvdXIgdGltZSBzZXJpZXMgaXMgbm9uLXN0YXRpb25hcnkgYW5kIHJlcXVpcmVzIGRpZmZlcmVuY2luZywgd2Ugd2lsbCBkbyB0aGF0IGJ5IGFwcGx5aW5nIGEgc2VyaWVzIG9mIEtQU1MgdGVzdHMuICAKCmBgYHtyfQpwMV90ZXN0ID0gdHNfcHJpY2VfbGlzdFtbIlJvYnVzdGFzIl1dJT4ldXIua3BzcygpCnAyX3Rlc3QgPSB0c19wcmljZV9saXN0W1siUm9idXN0YXMiXV0gJT4lIGRpZmYoKSAlPiUgdXIua3BzcygpCgpwMSA9IHRzX3ByaWNlX2xpc3RbWyJSb2J1c3RhcyJdXSU+JWF1dG9wbG90KCkrCiAgdGhlbWVfbWluaW1hbCgpKwogIGdndGl0bGUocGFzdGUwKCJQcmljZSwgS1BTUyBzdGF0aXN0aWM6ICIsIHJvdW5kKHAxX3Rlc3RAdGVzdHN0YXQsIDQpLCAiLCAxJSBzaWcuICIsIHAxX3Rlc3RAY3ZhbFs0XSkpCnAyID0gdHNfcHJpY2VfbGlzdFtbIlJvYnVzdGFzIl1dJT4lZGlmZigpJT4lYXV0b3Bsb3QoKSsKICB0aGVtZV9taW5pbWFsKCkrCiAgZ2d0aXRsZShwYXN0ZTAoIkZpcnN0IGRpZmZlcmVuY2UsIEtQU1Mgc3RhdGlzdGljOiAiLCByb3VuZChwMl90ZXN0QHRlc3RzdGF0LCA0KSwgIiwgMSUgc2lnLiAiLCBwMl90ZXN0QGN2YWxbNF0pKQoKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbmNvbD0xKQpgYGAKCgpgYGB7cn0KZGlmZmVyZW5jaW5nX3JlcXVpcmVkID0gbmRpZmZzKHRzX3ByaWNlX2xpc3RbWyJSb2J1c3RhcyJdXSkKc2Vhc29uYWxfZGlmZmVyZW5jaW5nX3JlcXVpcmVkID0gbnNkaWZmcyh0c19wcmljZV9saXN0W1siUm9idXN0YXMiXV0pCmBgYAoKS1BTUyB0ZXN0cyBzaG93ZWQgdGhhdCBgciBkaWZmZXJlbmNpbmdfcmVxdWlyZWRgIG1haW4gZGlmZmVyZW5jaW5nIGlzIHJlcXVpcmVkLCBidXQgbm8gc2Vhc29uYWwgZGlmZmVyZW5jaW5nIGlzIHJlcXVpcmVkIGZvciBwcmljZSB0aW1lIHNlcmllcyAobW9zdGx5IGR1ZSB0byBuZWdsaWdpYmxlIHNlYXNvbmFsIGVmZmVjdHMpLgoKQVJJTUEgbW9kZWxzIGhhdmUgb3JkZXIgcGFyYW1ldGVycyB7cCwgZCwgcX06ICAKLSAgcCAtIG9yZGVyIG9mIGF1dG9yZWdyZXNzaXZlIHBhcnQgIAotICBkIC0gZGVncmVlIG9mIGZpcnN0IGRpZmZlcmVuY2luZyBpbnZvbHZlZCAgCi0gIHEgLSBvcmRlciBvZiB0aGUgbW92aW5nIGF2ZXJhZ2UgcGFydCAgCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD02fQpjdXRvZmZfZGF0ZSA9IGMoMjAxNiw2KQoKcGxvdF9saXN0ID0gCiAgbGFwcGx5KAogICAgY29mZmVlX3R5cGVzLAogICAgZnVuY3Rpb24obmFtZSl7CiAgICAgIGEgPSBhdXRvLmFyaW1hKHdpbmRvdyh0c19wcmljZV9saXN0W1tuYW1lXV0sIGVuZD1jdXRvZmZfZGF0ZSkpCiAgICAgICNhID0gZm9yZWNhc3Q6OkFyaW1hKHdpbmRvdyh0c19wcmljZV9saXN0W1tuYW1lXV0sIGVuZD1jdXRvZmZfZGF0ZSksIG9yZGVyID0gYyg2LDEsMCkpCiAgICAgIG9yZGVyID0gYXJpbWFvcmRlcihhKQogICAgICAKICAgICAgcmV0dXJuKAogICAgICAgIGF1dG9wbG90KHRzX3ByaWNlX2xpc3RbW25hbWVdXSkrCiAgICAgICAgICBhdXRvbGF5ZXIoZm9yZWNhc3QoYSwgaD0yNCksIAogICAgICAgICAgICAgICAgICAgIHNlcmllcz1wYXN0ZTAoIkFSSU1BKCIsIHBhc3RlKGFyaW1hb3JkZXIoYSksIGNvbGxhcHNlPSIsIiksICIpIiksIAogICAgICAgICAgICAgICAgICAgIFBJPUZBTFNFKSsKICAgICAgICAgIHRoZW1lX21pbmltYWwoKQogICAgICApCiAgICB9CiAgKQoKZG8uY2FsbCgiZ3JpZC5hcnJhbmdlIiwgYyhwbG90X2xpc3QsIG5jb2wgPSAyKSkKYGBgCiMjIyBCYXNlbGluZSBhY2N1cmFjeSByZXN1bHRzOiAgIApgYGB7ciByZXN1bHRzPSdhc2lzJ30KYmFzaWNfYXJpbWFfZm9yZWNhc3QgPSBmdW5jdGlvbih0cywgaCl7CiAgYXJpbWFfZml0ID0gQXJpbWEodHMsIG9yZGVyID0gYygyLDEsMikpCiAgcmV0dXJuKGZvcmVjYXN0KGFyaW1hX2ZpdCwgaCA9IGgpKQp9Cgp0ZXN0X2FjY3VyYWN5ID0gZnVuY3Rpb24odHMsIGZvcmVjYXN0X2Z1bmMsIG5hbWUsIC4uLil7CiAgZSA9IHRzQ1YodHMsIGZvcmVjYXN0X2Z1bmMsIGg9NCwgd2luZG93ID0gMTIwLCAuLi4pCiAgUk1TRSA8LQogICAgZV4yICU+JSBtZWFuKG5hLnJtID0gVFJVRSkgJT4lIHNxcnQoKQogIAogIHJldHVybihkYXRhLnRhYmxlKE1vZGVsID0gbmFtZSwgUk1TRSA9IFJNU0UpKSAKfQoKdGVzdHMgPQpyYmluZGxpc3QoCiAgbGFwcGx5KAogICAgY29mZmVlX3R5cGVzLCAKICAgIGZ1bmN0aW9uKG5hbWUpewogICAgICByZXR1cm4odGVzdF9hY2N1cmFjeSh0c19wcmljZV9saXN0W1tuYW1lXV0sIGJhc2ljX2FyaW1hX2ZvcmVjYXN0LCAiQVJJTUEoMiwxLDIpIilbLCBDb2ZmZWVUeXBlOj1uYW1lXSkKICAgIH0KICApCikKCnRlc3RzICU+JSBkYXRhLnRhYmxlOjpkY2FzdCgKICBNb2RlbCB+IENvZmZlZVR5cGUsIHZhbHVlLnZhciA9ICJSTVNFIgopICU+JQogIGthYmxlKHR5cGUgPSAiaHRtbCIpJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIpKQpgYGAKCiMjIEFkZGluZyBjb3ZhcmlhdGVzIC0gQVJJTUFYIAoKIyMjIFByaWNlIGNyb3NzLWNvcnJlbGF0aW9ucyAgCkh5cG90aGVzaXM6IHByaWNlIHRpbWUgc2VyaWVzIGZvciB2YXJpb3VzIGNvZmZlZSB0eXBlcyBhcmUgY29pbnRlZ3JhdGVkLiAKCmBgYHtyfQpqb3Rlc3QgPSB1cmNhOjpjYS5qbygKICBkYXRhLmZyYW1lKAogICAgQnJhemlsaWFuTmF0dXJhbHMgPSBkaWZmKGFzLnZlY3Rvcih0c19wcmljZV9saXN0W1siQnJhemlsaWFuIE5hdHVyYWxzIl1dKSksCiAgICBDb2xvbWJpYW5NaWxkcyA9IGRpZmYoYXMudmVjdG9yKHRzX3ByaWNlX2xpc3RbWyJDb2xvbWJpYW4gTWlsZHMiXV0pKSwKICAgIE90aGVyTWlsZHMgPSBkaWZmKGFzLnZlY3Rvcih0c19wcmljZV9saXN0W1siT3RoZXIgTWlsZHMiXV0pKQogICksCiAgdHlwZSA9ICJ0cmFjZSIsCiAgSz0yLAogIGVjZGV0ID0gIm5vbmUiLAogIHNwZWMgPSAidHJhbnNpdG9yeSIKKQpzdW1tYXJ5KGpvdGVzdCkKYGBgClRlc3Qgc3RhdGlzdGljIGF0IHIgPSAwLCBzaG93cyB0aGF0IHRoZXJlCgojIyMgUHJpY2UgdG8gR3Jvd2VycyAgCkFsbCBvZiB0aGUgZm9sbG93aW5nIGNyb3NzIGNvcnJlbGF0aW9uIGFuYWx5c2lzIGlzIGRvbmUgdXNpbmcgInByZS13aGl0ZW5pbmciIGFwcHJvYWNoLCB3aXRoIHRoZSBmb2xsb3dpbmcgc3RlcHM6ICAKMS4gRGV0ZXJtaW5lIGEgdGltZSBzZXJpZXMgbW9kZWwgZm9yIHRoZSB4LXZhcmlhYmxlIGFuZCBzdG9yZSB0aGUgcmVzaWR1YWxzIGZyb20gdGhpcyBtb2RlbC4gICAgCjIuIEZpbHRlciB0aGUgeS12YXJpYWJsZSBzZXJpZXMgdXNpbmcgdGhlIHgtdmFyaWFibGUgbW9kZWwgKHVzaW5nIHRoZSBlc3RpbWF0ZWQgY29lZmZpY2llbnRzIGZyb20gc3RlcCAxKS4gSW4gdGhpcyBzdGVwIHdlIGZpbmQgZGlmZmVyZW5jZXMgYmV0d2VlbiBvYnNlcnZlZCB5LXZhbHVlcyBhbmQgImVzdGltYXRlZCIgeS12YWx1ZXMgYmFzZWQgb24gdGhlIHgtdmFyaWFibGUgbW9kZWwuICAKMy4gRXhhbWluZSBDRkYgYmV0d2VlbiB0aGUgcmVzaWR1YWxzIGZyb20gU3RlcCAxIGFuZCB0aGUgZmlsdGVyZWQgeS12YWx1ZXMgZnJvbSBTdGVwIDIuIFRoaXMgQ0ZGIGNhbiBiZSB1c2VkIHRvIGlkZW50aWZ5IHRoZSBwb3NzaWJsZSB0ZXJtcyBmb3IgYSBsYWdnZWQgcmVncmVzc2lvbi4gICAKYGBge3J9CmNoZWNrX2NjZiA9IGZ1bmN0aW9uKHRzX2xpc3QpewogIGxhcHBseSgKICAgIGNvZmZlZV90eXBlcywKICAgIGZ1bmN0aW9uKG5hbWUpewogICAgICBwdyA9IFRTQTo6cHJld2hpdGVuKAogICAgICAgIHRzX2xpc3RbW25hbWVdXSwKICAgICAgICB0c19wcmljZV9saXN0W1tuYW1lXV0KICAgICAgKQogICAgICAKICAgICAgcCA9IAogICAgICAgIGF1dG9wbG90KHB3JGNjZikrCiAgICAgICAgICB0aGVtZV9taW5pbWFsKCkrCiAgICAgICAgICBnZ3RpdGxlKG5hbWUpCiAgICAgIAogICAgICByZXR1cm4ocCkKICAgIH0KICApCn0KCnBsb3RfbGlzdCA9IGNoZWNrX2NjZih0c19ncm93ZXJfcHJpY2VzX21vbnRobHkpCgoKZG8uY2FsbCgiZ3JpZC5hcnJhbmdlIiwgYyhwbG90X2xpc3QsIG5jb2wgPSAyKSkKYGBgCgojIyMgUHJvZHVjdGlvbiAgCkxldCdzIGNoZWNrIGZvciBvcHRpbWFsIGxhZyB1c2luZyBDQ0Y6CgpgYGB7cn0KcGxvdF9saXN0ID0gY2hlY2tfY2NmKHRzX3Byb2R1Y3Rpb25fbW9udGhseV9saXN0KQoKZG8uY2FsbCgiZ3JpZC5hcnJhbmdlIiwgYyhwbG90X2xpc3QsIG5jb2wgPSAyKSkKYGBgClRoZXJlIGlzIG5vIGRlZmluaXRpdmUgY29ycmVsYXRpb24gc3RydWN0dXJlIGJldHdlZW4gcHJvZHVjdGlvbiBhbmQgcHJpY2UgZm9yIGFsbCBjb2ZmZWUgdHlwZXMsIHNvIG1vc3QgbGlrZWx5IGNvcnJlbGF0aW9uIHRoYXQgd2Ugc2VlIGFyZSBzcHVyaW91cy4gICAKCiMjIyBTdG9jayB0byBDb25zdW1wdGlvbiAgCkxldCdzIGNoZWNrIGZvciBvcHRpbWFsIGxhZyB1c2luZyBDQ0Y6CgpgYGB7cn0KcGxvdF9saXN0ID0gY2hlY2tfY2NmKHRzX3N0b2NrX3RvX2NvbnN1bXB0aW9uX21vbnRobHlfbGlzdCkKCmRvLmNhbGwoImdyaWQuYXJyYW5nZSIsIGMocGxvdF9saXN0LCBuY29sID0gMikpCmBgYApWZXJ5IGludGVyZXN0aW5nIGNvcnJlbGF0aW9uIHN0cnVjdHVyZSwgdGhhdCBzYXlzIHRoYXQgcHJpY2UgdG9kYXkgaXMgYmFzZWQgb24gdGhlIGVzdGltYXRlZCBhdmFpbGFibGUgc3RvY2sgNiBtb250aHMgZG93biB0aGUgbGluZS4gIFRoZSBvbmx5IGV4Y2VwdGlvbiBpcyBDb2xvbWJpYW4gTWlsZHMsIHdoaWNoIGRvbid0IHNob3cgc3Ryb25nIGNvcnJlbGF0aW9ucyB3aXRoaW4gcmVhc29uYWJsZSBsYWdzLiAgCgoKYGBge3J9CmNvZmZlZV90eXBlID0gIkJyYXppbGlhbiBOYXR1cmFscyIKCgoKbGFnZ2VkX3N0b2NrX2NvbnN1bXB0aW9uID0gZGF0YS50YWJsZSgKICBTdG9ja0NvbnN1bXB0aW9uTGFnMCA9IGFzLnZlY3Rvcih0c19zdG9ja190b19jb25zdW1wdGlvbl9tb250aGx5X2xpc3RbW2NvZmZlZV90eXBlXV0pCikKbGFnZ2VkX3N0b2NrX2NvbnN1bXB0aW9uWywgYDo9YCgKICBTdG9ja0NvbnN1bXB0aW9uTGFnMSA9IGRhdGEudGFibGU6OnNoaWZ0KFN0b2NrQ29uc3VtcHRpb25MYWcwLCB0eXBlID0gImxhZyIsIG49NiksCiAgU3RvY2tDb25zdW1wdGlvbkxhZzIgPSBkYXRhLnRhYmxlOjpzaGlmdChTdG9ja0NvbnN1bXB0aW9uTGFnMCwgdHlwZSA9ICJsYWciLCBuPTEyKSwKICBTdG9ja0NvbnN1bXB0aW9uTGFnMyA9IGRhdGEudGFibGU6OnNoaWZ0KFN0b2NrQ29uc3VtcHRpb25MYWcwLCB0eXBlID0gImxhZyIsIG49MTMpCildCgpmaXQxID0gQXJpbWEodHNfcHJpY2VfYnJhemlsaWFuX25hdHVyYWxzWzE0OjMzNl0sIHhyZWcgPSBsYWdnZWRfc3RvY2tfY29uc3VtcHRpb25bMTQ6MzM2LDFdLCBvcmRlciA9IGMoMCwxLDIpKQpmaXQyID0gQXJpbWEodHNfcHJpY2VfYnJhemlsaWFuX25hdHVyYWxzWzE0OjMzNl0sIHhyZWcgPSBsYWdnZWRfc3RvY2tfY29uc3VtcHRpb25bMTQ6MzM2LDJdLCBvcmRlciA9IGMoMCwxLDIpKQpmaXQzID0gQXJpbWEodHNfcHJpY2VfYnJhemlsaWFuX25hdHVyYWxzWzE0OjMzNl0sIHhyZWcgPSBsYWdnZWRfc3RvY2tfY29uc3VtcHRpb25bMTQ6MzM2LDNdLCBvcmRlciA9IGMoMCwxLDIpKQpmaXQ0ID0gQXJpbWEodHNfcHJpY2VfYnJhemlsaWFuX25hdHVyYWxzWzE0OjMzNl0sIHhyZWcgPSBsYWdnZWRfc3RvY2tfY29uc3VtcHRpb25bMTQ6MzM2LDRdLCBvcmRlciA9IGMoMCwxLDIpKQoKYyhmaXQxW1snYWljYyddXSwgZml0MltbJ2FpY2MnXV0sIGZpdDNbWydhaWNjJ11dLCBmaXQ0W1snYWljYyddXSkKCnN1bW1hcnkoZml0MSkKc3VtbWFyeShmaXQyKQpzdW1tYXJ5KGZpdDMpCnN1bW1hcnkoZml0NCkKYGBgCgpgYGB7cn0KYXJpbWF4X2ZvcmVjYXN0ID0gZnVuY3Rpb24odHMsIGgsIHhyZWcpewogIGFyaW1hX3hyZWdfZml0ID0gQXJpbWEodHMsIG9yZGVyID0gYygwLDEsMiksIHhyZWcgPSB4cmVnWzE6bGVuZ3RoKHRzKV0pCiAgcmV0dXJuKGZvcmVjYXN0KGFyaW1hX3hyZWdfZml0LCBoID0gaCwgeHJlZyA9IHhyZWdbKGxlbmd0aCh0cykrMSk6KGxlbmd0aCh0cykraCldKSkKfQoKdGVzdF9hY2N1cmFjeQoKZSA9IHRzQ1YoCiAgdHNfcHJpY2Vfcm9idXN0YXNbMTozMzZdLCAKICBhcmltYXhfZm9yZWNhc3QsCiAgaD00LCAKICB3aW5kb3cgPSAxMjAsCiAgeHJlZyA9IHRzX3N0b2NrX3RvX2NvbnN1bXB0aW9uX21vbnRobHlfbGlzdFtbIlJvYnVzdGFzIl1dKQoKZV4yICU+JSBtZWFuKG5hLnJtID0gVFJVRSkgJT4lIHNxcnQoKQpgYGAKCgoKCg==